#!/usr/bin/env python

"""encode/decode base58 in the same way that Bitcoin does"""

import hashlib
import math

__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
__b58base = len(__b58chars)

getNewRIPEMD160 = None
getNewSHA256 = None

def b58encode(v):
  """ encode v, which is a string of bytes, to base58.    
  """

  long_value = 0L
  for (i, c) in enumerate(v[::-1]):
    long_value += (256**i) * ord(c)

  result = ''
  while long_value >= __b58base:
    div, mod = divmod(long_value, __b58base)
    result = __b58chars[mod] + result
    long_value = div
  result = __b58chars[long_value] + result

  # Bitcoin does a little leading-zero-compression:
  # leading 0-bytes in the input become leading-1s
  nPad = 0
  for c in v:
    if c == '\0': nPad += 1
    else: break

  return (__b58chars[0]*nPad) + result

def b58decode(v, length):
  """ decode v into a string of len bytes
  """
  long_value = 0L
  for (i, c) in enumerate(v[::-1]):
    long_value += __b58chars.find(c) * (__b58base**i)

  result = ''
  while long_value >= 256:
    div, mod = divmod(long_value, 256)
    result = chr(mod) + result
    long_value = div
  result = chr(long_value) + result

  nPad = 0
  for c in v:
    if c == __b58chars[0]: nPad += 1
    else: break

  result = chr(0)*nPad + result
  if length is not None and len(result) != length:
    return None

  return result

def getNewRIPEMD160ByCrypto(public_key=""):
  return RIPEMD160.new(public_key)

def getNewRIPEMD160ByHashlib(public_key=""):
  newRIPEMD160 = hashlib.new('ripemd160')
  newRIPEMD160.update(public_key)
  return newRIPEMD160

def getNewSHA256ByCrypto(public_key=""):
  return SHA256.new(public_key)

def getNewSHA256ByHashlib(public_key=""):
  return hashlib.sha256(public_key)

try:
  # Python Crypto library is at: http://www.dlitz.net/software/pycrypto/
  # Needed for RIPEMD160 hash function, used to compute
  # Bitcoin addresses from internal public keys.
  import Crypto.Hash.RIPEMD160 as RIPEMD160
  getNewRIPEMD160 = getNewRIPEMD160ByCrypto
except ImportError:
  try:
    test = getNewRIPEMD160ByHashlib()
    getNewRIPEMD160 = getNewRIPEMD160ByHashlib
  except ImportError:
    print("Can not import RIPEMD160")

try:
  # Python Crypto library is at: http://www.dlitz.net/software/pycrypto/
  # Needed for RIPEMD160 hash function, used to compute
  # Bitcoin addresses from internal public keys.
  import Crypto.Hash.SHA256 as SHA256
  getNewSHA256 = getNewSHA256ByCrypto
except ImportError:
  try:
    test = getNewSHA256ByHashlib()
    getNewSHA256 = getNewSHA256ByHashlib
  except ImportError:
    print("Can not import SHA256")

def hash_160(public_key):
  if getNewSHA256 == None or getNewRIPEMD160 == None:
    return ''
  h1 = getNewSHA256(public_key).digest()
  h2 = getNewRIPEMD160(h1).digest()
  return h2

def public_key_to_bc_address(public_key):
  h160 = hash_160(public_key)
  return hash_160_to_bc_address(h160)

def hash_160_to_bc_address(h160):
  if getNewSHA256 == None:
    return ''
  vh160 = "\x00"+h160  # \x00 is version 0
  h3=getNewSHA256(getNewSHA256(vh160).digest()).digest()
  addr=vh160+h3[0:4]
  return b58encode(addr)

def bc_address_to_hash_160(addr):
  bytes = b58decode(addr, 25)
  return bytes[1:21]

if __name__ == '__main__':
    x = '005cc87f4a3fdfe3a2346b6953267ca867282630d3f9b78e64'.decode('hex_codec')
    encoded = b58encode(x)
    print encoded, '19TbMSWwHvnxAKy12iNm3KdbGfzfaMFViT'
    print b58decode(encoded, len(x)).encode('hex_codec'), x.encode('hex_codec')
